package com.atguigu.daijia.driver.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.dispatch.client.NewOrderFeignClient;
import com.atguigu.daijia.driver.service.OrderService;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.map.client.MapFeignClient;
import com.atguigu.daijia.model.entity.order.OrderInfo;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.map.CalculateDrivingLineForm;
import com.atguigu.daijia.model.form.order.OrderFeeForm;
import com.atguigu.daijia.model.form.order.StartDriveForm;
import com.atguigu.daijia.model.form.order.UpdateOrderBillForm;
import com.atguigu.daijia.model.form.order.UpdateOrderCartForm;
import com.atguigu.daijia.model.form.rules.FeeRuleRequestForm;
import com.atguigu.daijia.model.form.rules.ProfitsharingRuleRequestForm;
import com.atguigu.daijia.model.form.rules.RewardRuleRequestForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.map.DrivingLineVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.model.vo.order.*;
import com.atguigu.daijia.model.vo.rules.FeeRuleResponseVo;
import com.atguigu.daijia.model.vo.rules.ProfitsharingRuleResponseVo;
import com.atguigu.daijia.model.vo.rules.RewardRuleResponseVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.atguigu.daijia.rules.client.FeeRuleFeignClient;
import com.atguigu.daijia.rules.client.ProfitsharingRuleFeignClient;
import com.atguigu.daijia.rules.client.RewardRuleFeignClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

// 使用slf4j日志框架，方便记录日志信息
@Slf4j
// 标明该类是一个Spring服务类，用于处理订单相关的业务逻辑
@Service
// 忽略未经检查的警告，通常与泛型的动态调用有关，避免代码中出现不必要的警告信息
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderServiceImpl implements OrderService {

    // 注入NewOrderFeignClient客户端，用于调用新订单相关服务，通过Feign实现远程调用
    @Autowired
    private NewOrderFeignClient newOrderFeignClient;

    // 注入OrderInfoFeignClient客户端，用于调用订单信息相关服务，通过Feign实现远程调用
    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    /**
     * 获取订单状态
     *
     * @param orderId 订单ID，用于唯一标识一个订单
     * @return 订单状态，通常是一个整数表示不同状态，如已接单、已完成等
     */
    @Override
    public Integer getOrderStatus(Long orderId) {
        // 调用OrderInfoFeignClient的getOrderStatus方法获取订单状态，并返回其数据部分
        return orderInfoFeignClient.getOrderStatus(orderId).getData();
    }

    /**
     * 查询新订单队列数据
     *
     * @param driverId 司机ID，用于标识具体的司机
     * @return 新订单数据列表，包含司机可接的订单信息，如订单的基本信息、起始地点等
     */
    @Override
    public List<NewOrderDataVo> findNewOrderQueueData(Long driverId) {
        // 调用NewOrderFeignClient的findNewOrderQueueData方法查询新订单队列数据，并返回其数据部分
        return newOrderFeignClient.findNewOrderQueueData(driverId).getData();
    }

    /**
     * 清空新订单队列数据
     *
     * @param driverId 司机ID，用于标识具体的司机
     * @return 清空操作的结果，true表示成功，false表示失败
     */
    @Override
    public Boolean clearNewOrderQueueData(Long driverId) {
        // 调用NewOrderFeignClient的clearNewOrderQueueData方法清空新订单队列数据，并返回其数据部分
        return newOrderFeignClient.clearNewOrderQueueData(driverId).getData();
    }

    /**
     * 抢新订单
     *
     * @param driverId 司机ID，用于标识具体的司机
     * @param orderId  订单ID，用于唯一标识一个订单
     * @return 抢单操作的结果，true表示成功，false表示失败
     */
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        // 调用OrderInfoFeignClient的robNewOrder方法进行抢单操作，并返回其数据部分
        return orderInfoFeignClient.robNewOrder(driverId, orderId).getData();
    }

    /**
     * 根据客户ID查询客户当前订单信息
     *
     * @param customerId 客户ID，用于标识具体的客户
     * @return 当前订单信息视图对象，包含订单的详细信息
     */
    @Override
    public CurrentOrderInfoVo searchCustomerCurrentOrder(Long customerId) {
        // 调用OrderInfoFeignClient的searchCustomerCurrentOrder方法查询客户当前订单信息，并返回其数据部分
        return orderInfoFeignClient.searchCustomerCurrentOrder(customerId).getData();
    }

    /**
     * 根据司机ID查询司机当前订单信息
     *
     * @param driverId 司机ID，用于标识具体的司机
     * @return 当前订单信息视图对象，包含订单的详细信息
     */
    @Override
    public CurrentOrderInfoVo searchDriverCurrentOrder(Long driverId) {
        // 调用OrderInfoFeignClient的searchDriverCurrentOrder方法查询司机当前订单信息，并返回其数据部分
        return orderInfoFeignClient.searchDriverCurrentOrder(driverId).getData();
    }

    /**
     * 根据订单ID和司机ID获取订单信息
     *
     * @param orderId  订单ID，用于唯一标识一个订单
     * @param driverId 司机ID，用于标识具体的司机
     * @return 订单信息视图对象，包含订单的详细信息
     */
    @Override
    public OrderInfoVo getOrderInfo(Long orderId, Long driverId) {
        // 调用OrderInfoFeignClient的getOrderInfo方法获取订单信息
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        // 检查订单的司机ID是否与传入的司机ID一致，防止非法请求
        if (orderInfo.getDriverId().longValue() != driverId.longValue()) {
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        OrderBillVo orderBillVo=null;
        OrderProfitsharingVo orderProfitsharing = null;
        if(orderInfo.getStatus().intValue()>=
                OrderStatus.END_SERVICE.getStatus().intValue()){
            orderBillVo=orderInfoFeignClient.getOrderBillInfo(orderId).getData();
            orderProfitsharing=orderInfoFeignClient.getOrderProfitsharing(orderId).getData();
        }
        // 创建订单信息视图对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        // 设置订单ID
        orderInfoVo.setOrderId(orderId);
        // 将订单信息实体对象的属性复制到订单信息视图对象中
        BeanUtils.copyProperties(orderInfo, orderInfoVo);
        orderInfoVo.setOrderBillVo(orderBillVo);
        orderInfoVo.setOrderProfitsharingVo(orderProfitsharing);
        return orderInfoVo;
    }

    // 注入MapFeignClient客户端，用于调用地图相关服务，通过Feign实现远程调用
    @Autowired
    private MapFeignClient mapFeignClient;

    /**
     * 计算行驶路线
     *
     * @param calculateDrivingLineForm 计算行驶路线的表单对象，包含起始点、终点等信息
     * @return 行驶路线视图对象，包含路线的详细信息，如距离、时长等
     */
    @Override
    public DrivingLineVo calculateDrivingLine(CalculateDrivingLineForm calculateDrivingLineForm) {
        // 调用MapFeignClient的calculateDrivingLine方法计算行驶路线，并返回其数据部分
        return mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData();
    }

    /**
     * 司机到达起始位置
     *
     * @param orderId  订单ID，用于唯一标识一个订单
     * @param driverId 司机ID，用于标识具体的司机
     * @return 操作结果，true表示成功，false表示失败
     */
    @Override
    public Boolean driverArriveStartLocation(Long orderId, Long driverId) {
        // 调用OrderInfoFeignClient的getOrderInfo方法获取订单信息
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        // 调用LocationFeignClient的getCacheOrderLocation方法获取订单的缓存位置信息
        OrderLocationVo orderLocationVo = locationFeignClient.getCacheOrderLocation(orderId).getData();
        // 计算司机的位置与代驾起始点位置的距离
        double distance = LocationUtil.getDistance(orderInfo.getStartPointLatitude().doubleValue(), orderInfo.getStartPointLongitude().doubleValue(), orderLocationVo.getLatitude().doubleValue(), orderLocationVo.getLongitude().doubleValue());
        // 检查距离是否超过系统规定的最大距离，防止刷单
        if (distance > SystemConstant.DRIVER_START_LOCATION_DISTION) {
            throw new GuiguException(ResultCodeEnum.DRIVER_START_LOCATION_DISTION_ERROR);
        }
        // 调用OrderInfoFeignClient的driverArriveStartLocation方法通知司机已到达起始位置，并返回其数据部分
        return orderInfoFeignClient.driverArriveStartLocation(orderId, driverId).getData();
    }

    // 注入线程池执行器，用于异步执行任务
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    /**
     * 更新订单车辆信息
     *
     * @param updateOrderCartForm 更新订单车辆信息的表单对象，包含车辆的相关信息
     * @return 操作结果，true表示成功，false表示失败
     */
    @Override
    public Boolean updateOrderCart(UpdateOrderCartForm updateOrderCartForm) {
        // 调用OrderInfoFeignClient的updateOrderCart方法更新订单车辆信息，并返回其数据部分
        return orderInfoFeignClient.updateOrderCart(updateOrderCartForm).getData();
    }

    /**
     * 开始代驾
     *
     * @param startDriveForm 开始代驾的表单对象，包含订单ID、司机ID等信息
     * @return 操作结果，true表示成功，false表示失败
     */
    @Override
    public Boolean startDrive(StartDriveForm startDriveForm) {
        // 调用OrderInfoFeignClient的startDrive方法开始代驾，并返回其数据部分
        return orderInfoFeignClient.startDrive(startDriveForm).getData();
    }

    // 注入LocationFeignClient客户端，用于调用位置相关服务，通过Feign实现远程调用
    @Autowired
    private LocationFeignClient locationFeignClient;

    // 注入FeeRuleFeignClient客户端，用于调用费用规则相关服务，通过Feign实现远程调用
    @Autowired
    private FeeRuleFeignClient feeRuleFeignClient;

    // 注入RewardRuleFeignClient客户端，用于调用奖励规则相关服务，通过Feign实现远程调用
    @Autowired
    private RewardRuleFeignClient rewardRuleFeignClient;

    // 注入ProfitsharingRuleFeignClient客户端，用于调用分账规则相关服务，通过Feign实现远程调用
    @Autowired
    private ProfitsharingRuleFeignClient profitsharingRuleFeignClient;

    /**
     * 结束代驾
     *
     * @param orderFeeForm 订单费用表单对象，包含订单ID、司机ID、路桥费、停车费等信息
     * @return 操作结果，true表示成功，false表示失败
     * @throws Exception 可能抛出的异常
     */
    @SneakyThrows
    @Override
    public Boolean endDrive(OrderFeeForm orderFeeForm) {
        // 1.获取订单信息，使用CompletableFuture异步执行
        CompletableFuture<OrderInfo> orderInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 调用OrderInfoFeignClient的getOrderInfo方法获取订单信息
            OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData();
            return orderInfo;
        }, threadPoolExecutor);

        // 2.防止刷单，计算司机的经纬度与代驾的终点经纬度是否在2公里范围内，使用CompletableFuture异步执行
        CompletableFuture<OrderServiceLastLocationVo> orderServiceLastLocationVoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 调用LocationFeignClient的getOrderServiceLastLocation方法获取订单服务的最后位置信息
            OrderServiceLastLocationVo orderServiceLastLocationVo = locationFeignClient.getOrderServiceLastLocation(orderFeeForm.getOrderId()).getData();
            return orderServiceLastLocationVo;
        }, threadPoolExecutor);

        // 合并两个异步任务，等待它们都完成
        CompletableFuture.allOf(orderInfoCompletableFuture,
                orderServiceLastLocationVoCompletableFuture
        ).join();

        // 获取订单信息
        OrderInfo orderInfo = orderInfoCompletableFuture.get();
        // 2.1.判断刷单
        OrderServiceLastLocationVo orderServiceLastLocationVo = orderServiceLastLocationVoCompletableFuture.get();
        // 计算司机的位置与代驾终点位置的距离
        double distance = LocationUtil.getDistance(orderInfo.getEndPointLatitude().doubleValue(), orderInfo.getEndPointLongitude().doubleValue(), orderServiceLastLocationVo.getLatitude().doubleValue(), orderServiceLastLocationVo.getLongitude().doubleValue());
        // 检查距离是否超过系统规定的最大距离，防止刷单
        if (distance > SystemConstant.DRIVER_START_LOCATION_DISTION) {
            throw new GuiguException(ResultCodeEnum.DRIVER_END_LOCATION_DISTION_ERROR);
        }

        // 3.计算订单实际里程，使用CompletableFuture异步执行
        CompletableFuture<BigDecimal> realDistanceCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 调用LocationFeignClient的calculateOrderRealDistance方法计算订单实际里程
            BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData();
            log.info("结束代驾，订单实际里程：{}", realDistance);
            return realDistance;
        }, threadPoolExecutor);

        // 4.计算代驾实际费用，依赖于实际里程的计算结果，使用CompletableFuture异步执行
        CompletableFuture<FeeRuleResponseVo> feeRuleResponseVoCompletableFuture = realDistanceCompletableFuture.thenApplyAsync((realDistance) -> {
            // 创建费用规则请求表单对象
            FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
            // 设置距离
            feeRuleRequestForm.setDistance(realDistance);
            // 设置开始时间
            feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime());
            // 计算等候时间
            Integer waitMinute = Math.abs((int) ((orderInfo.getArriveTime().getTime() - orderInfo.getAcceptTime().getTime()) / (1000 * 60)));
            // 设置等候时间
            feeRuleRequestForm.setWaitMinute(waitMinute);
            log.info("结束代驾，费用参数：{}", JSON.toJSONString(feeRuleRequestForm));
            // 调用FeeRuleFeignClient的calculateOrderFee方法计算订单费用
            FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData();
            log.info("费用明细：{}", JSON.toJSONString(feeRuleResponseVo));
            // 订单总金额 需加上 路桥费、停车费、其他费用、乘客好处费
            BigDecimal totalAmount = feeRuleResponseVo.getTotalAmount().add(orderFeeForm.getTollFee()).add(orderFeeForm.getParkingFee()).add(orderFeeForm.getOtherFee()).add(orderInfo.getFavourFee());
            // 设置总金额
            feeRuleResponseVo.setTotalAmount(totalAmount);
            return feeRuleResponseVo;
        });

        // 5.计算系统奖励
        // 5.1.获取订单数，使用CompletableFuture异步执行
        CompletableFuture<Long> orderNumCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 格式化开始时间
            String startTime = new DateTime(orderInfo.getStartServiceTime()).toString("yyyy-MM-dd") + " 00:00:00";
            // 格式化结束时间
            String endTime = new DateTime(orderInfo.getStartServiceTime()).toString("yyyy-MM-dd") + " 24:00:00";
            // 调用OrderInfoFeignClient的getOrderNumByTime方法获取指定时间范围内的订单数
            Long orderNum = orderInfoFeignClient.getOrderNumByTime(startTime, endTime).getData();
            return orderNum;
        }, threadPoolExecutor);

        // 5.2.封装参数，依赖于订单数的计算结果，使用CompletableFuture异步执行
        CompletableFuture<RewardRuleResponseVo> rewardRuleResponseVoCompletableFuture = orderNumCompletableFuture.thenApplyAsync((orderNum) -> {
            // 创建奖励规则请求表单对象
            RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm();
            // 设置开始时间
            rewardRuleRequestForm.setStartTime(orderInfo.getStartServiceTime());
            // 设置订单数
            rewardRuleRequestForm.setOrderNum(orderNum);
            // 5.3.执行，调用RewardRuleFeignClient的calculateOrderRewardFee方法计算订单奖励费用
            RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData();
            log.info("结束代驾，系统奖励：{}", JSON.toJSONString(rewardRuleResponseVo));
            return rewardRuleResponseVo;
        });

        // 6.计算分账信息，依赖于费用规则响应和订单数的计算结果，使用CompletableFuture异步执行
        CompletableFuture<ProfitsharingRuleResponseVo> profitsharingRuleResponseVoCompletableFuture = feeRuleResponseVoCompletableFuture.thenCombineAsync(orderNumCompletableFuture, (feeRuleResponseVo, orderNum) -> {
            // 创建分账规则请求表单对象
            ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm();
            // 设置订单金额
            profitsharingRuleRequestForm.setOrderAmount(feeRuleResponseVo.getTotalAmount());
            // 设置订单数
            profitsharingRuleRequestForm.setOrderNum(orderNum);
            // 调用ProfitsharingRuleFeignClient的calculateOrderProfitsharingFee方法计算订单分账费用
            ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData();
            log.info("结束代驾，分账信息：{}", JSON.toJSONString(profitsharingRuleResponseVo));
            return profitsharingRuleResponseVo;
        });

        // 合并所有异步任务，等待它们都完成
        CompletableFuture.allOf(orderInfoCompletableFuture,
                realDistanceCompletableFuture,
                feeRuleResponseVoCompletableFuture,
                orderNumCompletableFuture,
                rewardRuleResponseVoCompletableFuture,
                profitsharingRuleResponseVoCompletableFuture
        ).join();

        // 获取执行结果
        BigDecimal realDistance = realDistanceCompletableFuture.get();
        FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoCompletableFuture.get();
        RewardRuleResponseVo rewardRuleResponseVo = rewardRuleResponseVoCompletableFuture.get();
        ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleResponseVoCompletableFuture.get();

        // 7.封装更新订单账单相关实体对象
        UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm();
        // 设置订单ID
        updateOrderBillForm.setOrderId(orderFeeForm.getOrderId());
        // 设置司机ID
        updateOrderBillForm.setDriverId(orderFeeForm.getDriverId());
        // 路桥费、停车费、其他费用
        updateOrderBillForm.setTollFee(orderFeeForm.getTollFee());
        updateOrderBillForm.setParkingFee(orderFeeForm.getParkingFee());
        updateOrderBillForm.setOtherFee(orderFeeForm.getOtherFee());
        // 乘客好处费
        updateOrderBillForm.setFavourFee(orderInfo.getFavourFee());
        // 实际里程
        updateOrderBillForm.setRealDistance(realDistance);
        // 订单奖励信息
        BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm);
        // 代驾费用信息
        BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm);
        // 分账相关信息
        BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm);
        // 设置分账规则ID
        updateOrderBillForm.setProfitsharingRuleId(profitsharingRuleResponseVo.getProfitsharingRuleId());
        log.info("结束代驾，更新账单信息：{}", JSON.toJSONString(updateOrderBillForm));

        // 8.结束代驾更新账单
        orderInfoFeignClient.endDrive(updateOrderBillForm);
        return true;
    }

    /**
     * 分页查询司机订单信息
     *
     * @param driverId 司机ID，用于标识具体的司机
     * @param page     页码，用于指定查询的页码
     * @param limit    每页数量，用于指定每页显示的订单数量
     * @return 分页视图对象，包含订单列表和分页信息
     */
    @Override
    public PageVo findDriverOrderPage(Long driverId, Long page, Long limit) {
        // 调用OrderInfoFeignClient的findDriverOrderPage方法分页查询司机订单信息，并返回其数据部分
        return orderInfoFeignClient.findDriverOrderPage(driverId, page, limit).getData();
    }

    @Override
    public Boolean sendOrderBillInfo(Long orderId, Long driverId) {
        return orderInfoFeignClient.sendOrderBillInfo(orderId, driverId).getData();
    }
}